﻿Imports Microsoft.VisualBasic
Imports System.Collections.Generic
Imports System.Drawing
''' <summary>
''' Class that provides access to shared methods to render chords and song wikis
''' </summary>
''' <remarks></remarks>
Public Class renderer
    ''' <summary>
    ''' Private shared member that stores the scaling factor.  This is re-initialised with each call of the renderChord function
    ''' </summary>
    ''' <remarks></remarks>
    Private Shared scalingFactor As Single
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="chordPattern">pattern in the format n1n2n3n4n5n6 where:
    ''' n1 = bottom E string fret position
    ''' n2 = A string fret position
    ''' n3 = D string fret position
    ''' n4 = G string fret position
    ''' n5 = B string fret position
    ''' n6 = top E string fret position
    ''' Positions are given as two digit numbers; 00 = open, xx = silent
    ''' </param>
    ''' <param name="chordTitle">Title of the chord, which will be shown at the top of the bitmap image</param>
    ''' <param name="size">size of bitmap (length of side of square, in pixels)</param>
    ''' <returns>A bitmap rendition of the chord</returns>
    ''' <remarks></remarks>
    Public Shared Function renderChord(ByVal chordPattern As String, ByVal chordTitle As String, ByVal size As Integer) As Bitmap
        scalingFactor = size / 1000

        'analyse chord pattern, and store positions in an array as follows
        Dim positions(5) As Integer 'array to hold position to be stopped on each guitar string.  -1 is used to indicate a "silent" string
        Dim positionString As String
        Dim positionMax As Integer = -1
        Dim positionMin As Integer = 99
        For c As Integer = 0 To 5
            positionString = Mid(chordPattern, c * 2 + 1, 2)
            If positionString = "xx" Then positions(c) = -1 Else positions(c) = CInt(positionString)
            If positions(c) < positionMin And positions(c) > 0 Then positionMin = positions(c)
            If positions(c) > positionMax Then positionMax = positions(c)
        Next
        Dim positionOffset As Integer = 0
        If positionMax > 4 Then
            positionOffset = positionMin - 1
        End If

        'set parameters for rendering the chord pattern
        Dim lineThickness As Single = 10 'line thickness of chord grid
        Dim spotDiameter As Single = 75 'diameter of spots showing finger positions
        Dim titleFontSize As Single = 120 'font size of chord title
        Dim labelFontSize As Single = 90 'font size of labels used on grid

        renderChord = New Bitmap(size, size)
        Dim myGraphics As Graphics = Graphics.FromImage(renderChord)

        'Draw basic grid of chord chart, add labels denoting guitar strings that are played open (O) or left silent (X), and add spots where strings are stopped

        Dim myPen As Pen = New Pen(Brushes.Black, scale(lineThickness))
        Dim i As Integer = 0
        Dim yOffset As Integer
        For x As Integer = 200 To 700 Step 100
            myGraphics.DrawLine(myPen, scale(x), scale(300), scale(x), scale(700))
            If positions(i) = -1 Then
                'guitar string is left silent - label with an X
                myGraphics.DrawString("X", New Font("Arial", scale(labelFontSize)), Brushes.Black, New Point(scale(x - 35), scale(150)))
            ElseIf positions(i) = 0 Then
                'guitar string is open - label with an O
                myGraphics.DrawString("O", New Font("Arial", scale(labelFontSize)), Brushes.Black, New Point(scale(x - 35), scale(150)))
            Else
                'guitar string is stopped at position(i) - draw a spot at the appropriate position
                yOffset = (100 * (positions(i) - positionOffset)) + 250 - spotDiameter / 2
                myGraphics.FillEllipse(Brushes.Black, New Rectangle(scale(x - spotDiameter / 2), scale(yOffset), scale(spotDiameter), scale(spotDiameter)))
            End If
            i = i + 1
        Next
        For y As Integer = 300 To 700 Step 100
            myGraphics.DrawLine(myPen, scale(200), scale(y), scale(700), scale(y))
        Next

        'Render title
        myGraphics.DrawString(chordTitle, New Font("Arial", scale(titleFontSize)), Brushes.Black, New Point(scale(20), scale(10)))

        'Render offset label if >0
        If positionOffset > 0 Then myGraphics.DrawString(positionOffset.ToString, New Font("Arial", scale(labelFontSize)), Brushes.Black, New Point(scale(20), scale(270)))


        Return renderChord
    End Function
    Private Shared Function scale(ByVal n As Single) As Single
        Return n * scalingFactor
    End Function
    Public Shared Function renderSong(ByVal wikiText As String) As String
        'the wiki text consists of lyrics interspersed with chord codes enclosed in square brackets
        '[ - opens a chord sequence
        '] - closes a chord sequence

        Dim pos As Integer = 0
        Dim endpos As Integer = 0
        Dim resultString As String = ""
        Dim mychordcode As String

        pos = InStr(wikiText, "[")
        If pos > 0 Then 'the wiki text contains at least one chord code, so process it to replace chord codes with HTML img tags to render the chord
            Do Until pos = 0
                'append text up to next chordCode start to result string
                resultString = resultString & Left(wikiText, pos - 1)
                'remove text up to and including chord code open bracket
                wikiText = Right(wikiText, wikiText.Length - pos)
                endpos = InStr(wikiText, "]")
                If endpos = 0 Then
                    'no closing bracket found, so we have a wiki that is not "well formed"
                    Return "<p>Unable to process wiki text because of an error.</p><p>Please check that all chord codes in the wiki text are formatted correctly and try again.</p>"
                Else
                    'we have found a closing bracket, so extract the chord code
                    mychordcode = Left(wikiText, endpos - 1)
                    'remove text up to and including chord code closing bracket
                    wikiText = Right(wikiText, wikiText.Length - endpos)
                    'add img tag from processed chord code
                    resultString = resultString & convertChordCodeToHTMLImgTag(mychordcode)
                End If
                pos = InStr(wikiText, "[")
            Loop
        Else
            Return wikiText
        End If
        'return the processed wiki text
        If Trim(wikiText) <> "" Then resultString = resultString & wikiText
        Return resultString
    End Function
    Private Shared Function convertChordCodeToHTMLImgTag(ByVal chordCode As String) As String
        If chordCode.Length < 14 Then Return "<img src=""images/error.png"" alt=""invalid code"" />"
        Dim chordPattern As String = Left(chordCode, 12)
        Dim chordTitle As String = Right(chordCode, chordCode.Length - 12)
        'encode chordTitle for query string
        chordTitle = System.Web.HttpUtility.UrlEncode(chordTitle)
        Return _
            "<img class=""chordSymbol"" src=""chord.aspx?size=80&chordPattern=" & _
            chordPattern & "&chordTitle=" & _
            System.Web.HttpUtility.UrlEncode(chordTitle) & """ alt=""" & System.Web.HttpUtility.UrlEncode(chordTitle) & """ />"
    End Function

End Class
